import numpy as np 
from MimicryHoneypot import *


class MemNode:
    item = None
    next_item = None
    def __init__(self, item):
        self.item = item


class MemLinkedList:
    head = None
    rear = None
    max_size = 0
    size = 0

    def __init__(self, max_size):
        self.max_size = max_size

    def push(self, item):
        if self.size >= self.max_size:
            self.pop()
        if self.head == None:
            self.head = MemNode(item)
            self.rear = self.head
        else:
            new_node = MemNode(item)
            self.rear.next_item = new_node
            self.rear = new_node
        self.size += 1

    def pop(self):
        if self.size <= 0:
            return None
        head_node = self.head
        self.head = self.head.next_item  
        if self.head == None:
            self.rear = None
        self.size -= 1
        return head_node

    def __str__(self) -> str:
        node = self.head
        ret = "head:"
        while(node != None):
            ret += str(node.item)
            node = node.next_item
        return ret

class Attacker:
    _max_interest = 100
    _min_insterest = 0
    _noise_rate = 0
    _drop_rate = 0.1
    


    def __init__(self, drop_rate=0.1,noise_rate=10, max_interest=100, min_insterest=0):
        self._noise_rate = noise_rate
        self._max_interest = max_interest
        self._min_insterest = min_insterest
        self._drop_rate = drop_rate

    def attack(self, target):
        pass


class AttackerLikeMaxDiff(Attacker):
    __cur_interest = 0
    __target_state = None
    __drop_speed = 0
    timer = 0
    def __init__(self,  drop_rate=0.1,noise_rate=0, max_interest=100, min_insterest=0):
        super().__init__(drop_rate=drop_rate,noise_rate=noise_rate, max_interest=max_interest, min_insterest=min_insterest)

    def keep_time(self):
        """get feature time, this time can should not be too long
        Returns:
            [type]: [description]
        """
        return 2

    def diff(self,state1,state2):
        length = len(list(state1))
        count = 0
        for i in range(length):
            if state1[i] != state2[i]:
                count += 1
        return (count * 1.0/ length)

    def updateInterest(self,new_state):
        if self.__target_state == None:
            self.__target_state = new_state
            self.__cur_interest = self._max_interest
            self.__drop_speed += self._drop_rate
            self.timer = self.keep_time()
            return self.__cur_interest
        
        # if attacker found a very different( 50% difference) honeypot,then increase his interest value
        if self.diff(new_state,self.__target_state) >0.7:
            noise = np.random.randn() * self._noise_rate
            interest = self.diff(new_state,self.__target_state) * (self._max_interest - self.__cur_interest) + self.__cur_interest
            self.__cur_interest =  interest
            self.__target_state = new_state
            self.__drop_speed = self._drop_rate
            self.timer = self.keep_time()
            return self.__cur_interest + noise
        # honeypot's feature didn't change or just changed a little
        else:
            self.timer -= 1
            if self.timer > 0:
                return self.__cur_interest + self._noise_rate * np.random.randn()
            else:
                self.__drop_speed += self._drop_rate
                self.__cur_interest -= self.__drop_speed 

                if self.__cur_interest < self._min_insterest:
                    self.__cur_interest = self._min_insterest
                    self.timer = 0
                return self.__cur_interest - self._noise_rate * np.random.randn()
            
            
    def attack(self, state):
        interest = self.updateInterest(state)

        return interest


